package com.lingjtx.common.core.util;

import com.lingjtx.common.core.RedisOptions;
import com.lingjtx.common.core.algorithm.MD5Util;
import com.lingjtx.common.core.constant.RedisConfig;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class ParamCheck {

    private final String sign;

    private final String requestUri;

    private final RedisOptions redisOptions;

    private final Object userId;

    private final long expireTime;

    public static ParamCheck.Builder init() {
        return new ParamCheck.Builder();
    }

    public ParamCheck(ParamCheck.Builder builder) {
        this.sign = builder.sign;
        this.redisOptions = builder.redisOptions;
        this.userId = builder.userId;
        this.requestUri = builder.requestUri;
        this.expireTime = builder.expireTime;
    }

    /**
     * 是否重复提交
     *
     * @return true-重复提交；false-没有重复提交
     */
    public boolean repeatable() {
        if (this.redisOptions == null
                || this.requestUri == null
                || this.userId == null)
            throw new NullPointerException("你没有添加userId或redisService或requestUri");
        return !add(this.generateKey(), this.sign, this.expireTime);
    }

    private String generateKey() {
        return RedisConfig.NON_REPEATABLE + this.requestUri + "-" + this.userId;
    }

    private boolean add(String key, Object value, long timeout) {
        if (Objects.isNull(key)) return false;
        Long res = this.redisOptions.sAdd(key, value);
        if (this.redisOptions.getExpire(key) < 0) { // 第一次设置过期时间
            this.redisOptions.expire(key, timeout, TimeUnit.SECONDS);
        }
        return Objects.equals(1L, res);
    }

    public static final class Builder {
        private static final String TIMESTAMP_KEY = "timestamp";
        private static final String SIGNATURE_KEY = "signature";

        private Map<String, Object> map;

        private String sign;

        private String requestUri;

        private RedisOptions redisOptions;

        private Object userId;

        private long expireTime;

        public Builder() {
        }

        public Builder(Map<String, Object> map) {
            this.map = map;
        }

        public Builder addParamMap(Map<String, Object> map) {
            this.map = map;
            return this;
        }

        public Builder addUserUid(Object arg) {
            map.put("userUid", arg);
            userId = arg;
            return this;
        }

        public Builder addRequestUri(String requestUri) {
            this.requestUri = requestUri;
            return this;
        }

        public Builder addRedisService(RedisOptions redisOptions) {
            this.redisOptions = redisOptions;
            return this;
        }

        public Builder addExpireTime(long expireTime) {
            this.expireTime = expireTime;
            return this;
        }

        public Builder remove(String... keys) {
            for (String key : keys) {
                if (map.containsKey(key))
                    map.remove(key);
            }
            return this;
        }

        public Builder removeTimestamp() {
            this.remove(TIMESTAMP_KEY);
            return this;
        }

        public Builder removeSignature() {
            this.remove(SIGNATURE_KEY);
            return this;
        }

        public Builder removeDefaultKey() {
            this.remove(TIMESTAMP_KEY, SIGNATURE_KEY);
            return this;
        }

        public ParamCheck hash() {
            sign = String.valueOf(map.hashCode());
            return new ParamCheck(this);
        }

        public ParamCheck md5() {
            StringBuffer str = new StringBuffer();
            map.forEach((key, value) -> str.append("&").append(key).append("=").append(value.toString()));
            sign = MD5Util.MD5EncodeUtf8(str.substring(1));
            return new ParamCheck(this);
        }
    }
}
